package com.logic.landseaserver.common.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import com.logic.landseaserver.common.enums.WeBankMsgEnum;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

@Service
public class WeBankQRUtil implements EnvironmentAware
{
    private static final Logger LOG = LoggerFactory.getLogger(WeBankQRUtil.class);
    
    public static String webank_access_token;
    
    public static String ticket;
    
    public static String ticket_endtime;
    
    private static String app_id;
    
    private static String secret;
    
    private static String root_url;
    
    private static String merchantId;
    
    private static String merchantName;
    
    private static String callback_url;
    
    private static String QR_p12_path;
    
    private static String QR_jks_path;
    
    private static String key_store_password;
    
    private static String key_store_trust_password;
    
    private static String version;
    
    private static String pay_url;
    
    private static RelaxedPropertyResolver weBankpropertyResolver;
    
    private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
    
    private static final String ENV_WEBAK = "logic.webankQR.";
    
    private static final String APPID = "app_id";
    
    private static final String SECRET = "secret";
    
    private static final String ROOT_URL = "root_url";
    
    private static final String CALLBACK_URL = "callback_url";
    
    private static final String MERCHANTID = "merchantId";
    
    private static final String MERCHANTNAME = "merchantName";
    
    private static final String VERSION = "version";
    
    private static final String WEBANKQR_KEY_STORE_PASSWORD = "key_store_password";
    
    private static final String WEBANKQR_KEY_STORE_TRUST_PASSWORD = "key_store_trust_password";
    
    private static final String QR_P12_PATH = "p12_path";
    
    private static final String QR_JKS_PATH = "jks_path";
    
    private static final String PAY_URL = "pay_url";
    
    public static String access_token_time;
    
    public static long validateTimeLang;
    
    public static String ACCESS_TOKEN_URL = "/api/oauth2/access_token";
    
    public static String TICKET_URL = "/api/oauth2/api_ticket";
    
    public static String PAY_CHECK_URL = "/api/aap/server/wepay/querytrade";
    
    private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);
    
    private static WeBankQRUtil instance = new WeBankQRUtil();
    
    private WeBankQRUtil()
    {
        
    }
    
    public static WeBankQRUtil getInstance()
    {
        return instance;
    }
    
    public String getAccessToken()
        throws Exception
    {
        if (webank_access_token == null)
        {
            setAccessToken();
        }
        else
        {
            boolean validate = validateAccessToken();
            if (!validate)
            {
                setAccessToken();
            }
        }
        return webank_access_token;
    }
    
    // 获取access_token
    public void setAccessToken()
        throws Exception
    {
        if (app_id == null || secret == null || root_url == null)
        {
            readProperties();
            settoken();
        }
        else
        {
            settoken();
        }
    }
    
    public String getApp_id()
    {
        if (null == app_id)
        {
            readProperties();
        }
        return app_id;
    }
    
    public String getSecret()
    {
        if (null == secret)
        {
            readProperties();
        }
        return secret;
    }
    
    public String getVersion()
    {
        if (null == version)
        {
            readProperties();
        }
        return version;
    }
    
    public String getRoot_url()
    {
        if (null == root_url)
        {
            readProperties();
        }
        return root_url;
    }

    public String getMerchantId()
    {
        if (null == merchantId)
        {
            readProperties();
        }
        return merchantId;
    }
    
    public String getMerchantName()
    {
        if (null == merchantName)
        {
            readProperties();
        }
        return merchantName;
    }
    
    public String getCallback_url()
    {
        if (null == callback_url)
        {
            readProperties();
        }
        return callback_url;
    }
    
    public String getPay_check_url()
    {
        return PAY_CHECK_URL;
    }
    
    public String getPay_url()
    {
        if (null == pay_url)
        {
            readProperties();
        }
        return pay_url;
    }
    
    public void settoken()
        throws Exception
    {
        readProperties();
        String result = SSLGetRequest(root_url + ACCESS_TOKEN_URL + "?app_id=" + app_id + "&secret=" + secret
            + "&grant_type=client_credential&version=1.0.0");
        JSONObject tokenjson = JSONObject.fromObject(result);
        if (tokenjson.containsKey("code"))
        {
            if ("0".equals(tokenjson.getString("code")))
            {
                webank_access_token = tokenjson.getString("access_token");
                LOG.info("access_token: " + webank_access_token);
                Date nowTime = new Date(System.currentTimeMillis());
                SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                access_token_time = sdFormatter.format(nowTime);
                validateTimeLang = Long.parseLong(tokenjson.getString("expire_in")) - 600; // 提前10分钟刷新access_token
                logger.info(
                    "---------------------获取微众银行收单access_token--------------------------获取微众银行收单access_token-------------------------");
            }
            else
            {
                String errormsg = null;
                errormsg = WeBankMsgEnum.valueOf("_" + tokenjson.getString("code")).getCode();
                logger.error("微众端错误：" + errormsg);
                throw new Exception("微众端错误：" + errormsg);
            }
        }
    }
    
    // 读取配置文件
    public void readProperties()
    {
        if (app_id == null || secret == null || root_url == null || merchantId == null || merchantName == null
            || version == null)
            try
            {
                /*
                 * app_id = "W0537510"; secret = "9tzKhSaRxng7MIgY4ASp0CZtUIhEcRoArJTqI5ujO4zd0KYEc9Z82pArhKaDQjSl";
                 * root_url = "https://l.test-svrapi.webank.com"; merchantId = "202290070110001"; merchantName =
                 * "上海朗诗寓商业管理有限公司"; version = "1.0.0"; callback_url= "http://www.baidu.com"; pc_QR_url=
                 * "https://test-open.webank.com/s/web-beatle-pc/index.html#!/home/creditCard/";
                 */
                
                app_id = weBankpropertyResolver.getProperty(APPID, String.class);
                secret = weBankpropertyResolver.getProperty(SECRET, String.class);
                root_url = weBankpropertyResolver.getProperty(ROOT_URL, String.class);
                merchantId = weBankpropertyResolver.getProperty(MERCHANTID, String.class);
                merchantName = weBankpropertyResolver.getProperty(MERCHANTNAME, String.class);
                version = weBankpropertyResolver.getProperty(VERSION, String.class);
                callback_url = weBankpropertyResolver.getProperty(CALLBACK_URL, String.class);
                QR_p12_path = weBankpropertyResolver.getProperty(QR_P12_PATH, String.class);
                QR_jks_path = weBankpropertyResolver.getProperty(QR_JKS_PATH, String.class);
                key_store_password = weBankpropertyResolver.getProperty(WEBANKQR_KEY_STORE_PASSWORD, String.class);
                key_store_trust_password =
                    weBankpropertyResolver.getProperty(WEBANKQR_KEY_STORE_TRUST_PASSWORD, String.class);
                pay_url = weBankpropertyResolver.getProperty(PAY_URL, String.class);
                
            }
            catch (Exception e)
            {
                LOG.error("readProperties error.", e);
            }
    }
    
    // 验证access_token时间
    public boolean validateAccessToken()
        throws ParseException
    {
        
        Date nowTime = new Date(System.currentTimeMillis());
        SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdFormatter.format(nowTime);
        
        Date d1 = sdFormatter.parse(time);
        Date d2 = sdFormatter.parse(access_token_time);
        long diff = d1.getTime() - d2.getTime();// 这样得到的差值是微秒级别
        long seconds = (diff) / (1000);
        
        if (seconds < validateTimeLang)
        {
            return true;
        }
        return false;
    }
    
    // ------------------------------------------------------------------------------------------------------------------------------
    
    public void setTicket()
        throws Exception
    {
        String token = getAccessToken();
        readProperties();
        String result = SSLGetRequest(
            root_url + TICKET_URL + "?app_id=" + app_id + "&access_token=" + token + "&type=SIGN&version=1.0.0");
        JSONObject ticketjson = JSONObject.fromObject(result);
        if (ticketjson.containsKey("code"))
        {
            if ("0".equals(ticketjson.getString("code")))
            {
                JSONArray tickets = ticketjson.getJSONArray("tickets");
                JSONObject signticket = tickets.getJSONObject(0);
                String endtime = signticket.getString("expire_time");
                SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                endtime = sdFormatter.format(new Long(endtime));
                // HashMap<String,String> ticketinfo = new HashMap();
                
                LOG.info("ticket: " + signticket.getString("value"));
                
                // ticketinfo.put("ticket", signticket.getString("value"));
                // ticketinfo.put("expire_time", endtime);
                
                ticket = signticket.getString("value");
                ticket_endtime = endtime;
                /*
                 * if(ticketMap.size()>=1000){ //不能超过1000条 ticketMap.clear(); } ticketMap.put(userId, ticketinfo);
                 */
                logger.info(
                    "---------------------获取微众银行收单ticket--------------------------获取微众银行收单ticket---------------------------");
            }
            else
            {
                String errormsg = null;
                errormsg = WeBankMsgEnum.valueOf("_" + ticketjson.getString("code")).getCode();
                logger.error("微众端错误：" + errormsg);
                throw new Exception("微众端错误：" + errormsg);
            }
        }
    }
    
    // 获取前端ticket
    public String getOnceTicket(String userId)
        throws Exception
    {
        String token = getAccessToken();
        readProperties();
        String result = SSLGetRequest(root_url + TICKET_URL + "?app_id=" + app_id + "&access_token=" + token
            + "&type=NONCE&version=1.0.0&user_id=" + userId);
        JSONObject ticketjson = JSONObject.fromObject(result);
        String onceTicket = null;
        if (ticketjson.containsKey("code"))
        {
            if ("0".equals(ticketjson.getString("code")))
            {
                JSONArray tickets = ticketjson.getJSONArray("tickets");
                JSONObject signticket = tickets.getJSONObject(0);
                String endtime = signticket.getString("expire_time");
                SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                endtime = sdFormatter.format(new Long(endtime));
                onceTicket = signticket.getString("value");
                
            }
            else
            {
                String errormsg = null;
                errormsg = WeBankMsgEnum.valueOf("_" + ticketjson.getString("code")).getCode();
                logger.error("微众端错误：" + errormsg);
                throw new Exception("微众端错误：" + errormsg);
            }
        }
        return onceTicket;
    }
    
    // 检验ticket有效性
    public static boolean validateTicket(String ticketEndTime)
        throws ParseException
    {
        Date nowTime = new Date(System.currentTimeMillis());
        SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdFormatter.format(nowTime);
        
        Date d1 = sdFormatter.parse(time);
        Date d2 = sdFormatter.parse(ticketEndTime);
        long diff = d2.getTime() - d1.getTime();// 这样得到的差值是微秒级别
        long seconds = (diff) / (1000);
        
        if (seconds < 600)
        { // 提前10分钟
            return false;
        }
        return true;
    }
    
    // --------------------------------------------------------------------------------------------------------------------------------
    // 获取签名算法
    public static String sign(List<String> values, String signTicket)
    {
        if (values == null)
        {
            throw new NullPointerException("values is null");
        }
        
        values.removeAll(Collections.singleton(null));// remove null
        values.add(signTicket);
        Collections.sort(values);
        
        StringBuilder sb = new StringBuilder();
        for (String s : values)
        {
            sb.append(s);
        }
        try
        {
            MessageDigest md = MessageDigest.getInstance("sha1");
            md.update(sb.toString().getBytes("UTF-8"));
            String nativeSign = bytesToHex(md.digest());
            return nativeSign;
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
    
    private static String bytesToHex(byte[] bytes)
    {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++)
        {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }
    
    // 生成32位随机字符串
    public static String getNonceStr()
    {
        String nonceStr = UUID.randomUUID().toString().substring(0, 32);
        return nonceStr;
    }
    
    // 设置代理
    public static HttpClient getHttpClient()
    {
        
        DefaultHttpClient httpClient = new DefaultHttpClient();
        // 不是所有都需要用到代理商号
        String proxyHost = "119.29.195.110";// IP与端口固定为此值
        int proxyPort = 8080;
        String userName = "UXXXXX.p12";// "userName";//需替换成实际帐号名
        String password = "Abcd1234";// "password";//需替换成实际密码
        httpClient.getCredentialsProvider().setCredentials(new AuthScope(proxyHost, proxyPort),
            new UsernamePasswordCredentials(userName, password));
        HttpHost proxy = new HttpHost(proxyHost, proxyPort);
        httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, proxy);
        return httpClient;
    }
    
    // GET请求发送
    public static String SSLGetRequest(String url)
        throws Exception
    {
        
        // 设置SSL证书
        String KEY_STORE_PASSWORD = key_store_password;
        String KEY_STORE_TRUST_PASSWORD = key_store_trust_password;
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        KeyStore trustStore = KeyStore.getInstance("JKS");
        
        InputStream ksIn = WeBankQRUtil.class.getResourceAsStream(QR_p12_path); // 客户端证书
        InputStream tsIn = WeBankQRUtil.class.getResourceAsStream(QR_jks_path); // 服务器证书
        
        try
        {
            keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
            trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
        }
        catch (Exception e)
        {
            logger.error("微众获取access_token报错：" + e.getMessage());
        }
        finally
        {
            ksIn.close();
            tsIn.close();
        }
        
        HttpGet httpgGet = new HttpGet(url);
        
        SSLContext sslcontext = null;
        sslcontext = SSLContexts.custom()
            .loadKeyMaterial(keyStore, KEY_STORE_PASSWORD.toCharArray())
            .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
            .build();
        
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] {"TLSv1"}, null,
            SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        
        /*
         * CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( new
         * AuthScope("119.29.195.110", 8080), new UsernamePasswordCredentials("UXXXXX.p12", "Abcd1234"));
         */
        
        // HttpClient httpclient = getHttpClient();
        HttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        
        /*
         * CloseableHttpClient httpclient = HttpClientUtils.createHttpClientWithCert(keyStore, KEY_STORE_PASSWORD,
         * trustStore, 20, 10, 100, 100, "119.29.195.110", 8080, "UXXXXX.p12", "Abcd1234");
         */
        
        HttpResponse response = httpclient.execute(httpgGet);
        HttpEntity entity = response.getEntity();
        
        String respContent = EntityUtils.toString(entity, "UTF-8").trim();
        logger.info("返回值：" + respContent);
        return respContent;
    }
    
    // POST请求发送
    public static String SSLPostRequest(String url, String requestJson)
        throws Exception
    {
        String result = null;
        try
        {
            
            HttpPost httpPost = new HttpPost(url);
            
            StringEntity reqentity = new StringEntity(new String(requestJson.getBytes("UTF-8")), "UTF-8");
            
            reqentity.setContentType("application/json;charset=UTF-8");
            
            httpPost.setEntity(reqentity);
            
            ////////////////////////////////////////////////////////////////
            // 此处加入https双向认证
            String KEY_STORE_PASSWORD = key_store_password;
            String KEY_STORE_TRUST_PASSWORD = key_store_trust_password;
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            KeyStore trustStore = KeyStore.getInstance("JKS");
            
            InputStream ksIn = WeBankQRUtil.class.getResourceAsStream(QR_p12_path); // 客户端证书
            InputStream tsIn = WeBankQRUtil.class.getResourceAsStream(QR_jks_path); // 服务器证书
            
            try
            {
                keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
                trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
            }
            catch (Exception e)
            {
                logger.error("got a exception" + e.getMessage());
            }
            finally
            {
                ksIn.close();
                tsIn.close();
            }
            
            SSLContext sslcontext = null;
            sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, KEY_STORE_PASSWORD.toCharArray())
                .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
                .build();
            
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] {"TLSv1"}, null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
            
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            
            HttpResponse response = httpclient.execute(httpPost);
            
            HttpEntity entity = response.getEntity();
            
            String respContent = EntityUtils.toString(entity, "UTF-8").trim();
            
            result = respContent;
            
        }
        catch (MalformedURLException e)
        {
            LOG.error("SSLPostRequest error.", e);
        }
        catch (IOException e)
        {
            LOG.error("SSLPostRequest error.", e);
        }
        return result;
        
    }
    
    @Override
    public void setEnvironment(Environment environment)
    {
        weBankpropertyResolver = new RelaxedPropertyResolver(environment, ENV_WEBAK);
    }
    
}
